#include "ModifiesHandle.h"


ModifiesHandle::ModifiesHandle(void) {}

ModifiesHandle::~ModifiesHandle(void) {}

bool ModifiesHandle::MergeMultiVector(vector<vector<int>> &head, vector<vector<int>> tail) 
{
	unsigned int i=0;
	for(; i<head.size(); i++)
		head[i].insert(head[i].end(), tail[i].begin(), tail[i].end());
	
	return true;
}

vector<vector<int>> ModifiesHandle::FetchValidModifiesStatementVar(vector<int> stmts_in, vector<int> var_in) 
{
	vector<vector<int>> result_set;
	vector<int> left, right;

	for (auto iter_s=stmts_in.begin(); iter_s!=stmts_in.end(); iter_s++) {
		for( auto iter_v=var_in.begin(); iter_v!=var_in.end(); iter_v++) {

			STMT temp;
			temp.statement_number= *iter_s;
			if(PKB::modifies.GetStatementModifies(temp, *iter_v)) {
				left.push_back(*iter_s);
				right.push_back(*iter_v);
			}
		}
	}
		
	result_set.push_back(left);
	result_set.push_back(right);

	return result_set;
}

vector<vector<int>> ModifiesHandle::FetchValidModifiesProcedureVar(vector<int> procs_in, vector<int> var_in)
{
	vector<vector<int>> result_set;
	vector<int> left, right;

	for (auto iter_s=procs_in.begin(); iter_s!=procs_in.end(); iter_s++) {
		for( auto iter_v=var_in.begin(); iter_v!=var_in.end(); iter_v++) {

			if(PKB::modifies.GetProcedureModifies(*iter_s, *iter_v)) {
				left.push_back(*iter_s);
				right.push_back(*iter_v);
			}
		}
	}
		
	result_set.push_back(left);
	result_set.push_back(right);

	return result_set;
	
}

vector<vector<int>> ModifiesHandle::GetStatementModifiesVar(vector<int> stmts_in, vector<int> vars_in)
{
	assert (((stmts_in.size() > 0) && vars_in.empty())
		|| (stmts_in.empty() && (vars_in.size() > 0)));

	vector<vector<int>> sub_result, result;

	if (stmts_in.size() > 0) {
		for (auto iter = stmts_in.begin(); iter != stmts_in.end(); iter++) {
			// For each statement, fetch vars that it modifies
			sub_result = this->ModVarsGivenStatement(*iter);
			MergeMultiVector(result, sub_result);
		}
	} else {
		for (auto iter_v = vars_in.begin(); iter_v != vars_in.end(); iter_v++) {
			// For each var, fetch statements that modify it
			sub_result = this->ModStatementsGivenVar(*iter_v);
			this->MergeMultiVector(result, sub_result);
		}
	}
	return result;

}

vector<vector<int>> ModifiesHandle::GetAssignmentModifiesVar(vector<int> stmts_in, vector<int> vars_in)
{
	assert (((stmts_in.size() > 0) && vars_in.empty())
		|| (stmts_in.empty() && (vars_in.size() > 0)));

	vector<vector<int>> sub_result, result;
	if (stmts_in.size() > 0) {
		for (auto iter = stmts_in.begin(); iter != stmts_in.end(); iter++) {
			// For each assignment, fetch vars that it modifies
			sub_result = this->ModVarsGivenStatement(*iter);
			this->MergeMultiVector(result, sub_result);
		}
	} else {
		for (auto iter_v = vars_in.begin(); iter_v != vars_in.end(); iter_v++) {
			// For each var, fetch assignments that modifies it
			sub_result = this->ModAssignmentsGivenVar(*iter_v);
			this->MergeMultiVector(result, sub_result);
		}
	}
	return result;
}

vector<vector<int>> ModifiesHandle::GetIfModifiesVar(vector<int> stmts_in, vector<int> vars_in)
{
	assert (((stmts_in.size() > 0) && vars_in.empty())
		|| (stmts_in.empty() && (vars_in.size() > 0)));

	vector<vector<int>> sub_result, result;
	if (stmts_in.size() > 0) {
		for (auto iter = stmts_in.begin(); iter != stmts_in.end(); iter++) {
			// For each ifstmt, fetch vars that it modifies
			sub_result = this->ModVarsGivenStatement(*iter);
			this->MergeMultiVector(result, sub_result);
		}
	} else {
		for (auto iter_v = vars_in.begin(); iter_v != vars_in.end(); iter_v++) {
			// For each var, fetch if stmts that modify it
			sub_result = this->ModIfGivenVar(*iter_v);
			this->MergeMultiVector(result, sub_result);
		}
	}
	return result;

}

vector<vector<int>> ModifiesHandle::GetWhileModifiesVar(vector<int> stmts_in, vector<int> vars_in)
{
	assert (((stmts_in.size() > 0) && vars_in.empty())
		|| (stmts_in.empty() && (vars_in.size() > 0)));

	vector<vector<int>> sub_result, result;
	if (stmts_in.size() > 0) {
		for (auto iter = stmts_in.begin(); iter != stmts_in.end(); iter++) {
			// For each while stmt, fetch vars that it modifies
			sub_result = this->ModVarsGivenStatement(*iter);
			this->MergeMultiVector(result, sub_result);
		}
	} else {
		for (auto iter_v = vars_in.begin(); iter_v != vars_in.end(); iter_v++) {
			// For each var, fetch while stmts that modify it
			sub_result = this->ModWhileGivenVar(*iter_v);
			this->MergeMultiVector(result, sub_result);
		}
	}
	return result;	
}

vector<vector<int>> ModifiesHandle::GetContainerModifiesUsesVar(vector<int> stmts_in, vector<int> vars_in)
{
	assert (((stmts_in.size() > 0) && vars_in.empty())
		|| (stmts_in.empty() && (vars_in.size() > 0)));

	vector<vector<int>> sub_result, result;
	if (stmts_in.size() > 0) {
		for (auto iter = stmts_in.begin(); iter != stmts_in.end(); iter++) {
			// For each container stmt, fetch vars that it modifies
			sub_result = this->ModVarsGivenStatement(*iter);
			this->MergeMultiVector(result, sub_result);
		}
	} else {
		for (auto iter_v = vars_in.begin(); iter_v != vars_in.end(); iter_v++) {
			// For each var, fetch containers that modify it
			sub_result = this->ModContainerGivenVar(*iter_v);
			this->MergeMultiVector(result, sub_result);
		}
	}
	return result;
}

vector<vector<int>> ModifiesHandle::GetProcedureModifiesVar(vector<int> proc_in, vector<int> vars_in)
{
	assert (((proc_in.size() > 0) && vars_in.empty())
		|| (proc_in.empty() && (vars_in.size() > 0)));

	vector<vector<int>> sub_result, result;
	if (proc_in.size() > 0) {
		for (auto iter = proc_in.begin(); iter != proc_in.end(); iter++) {
			// For each proc, fetch vars that it modifies
			sub_result = this->ModVarsGivenStatement(*iter);
			this->MergeMultiVector(result, sub_result);
		}
	} else {
		for (auto iter_v = vars_in.begin(); iter_v != vars_in.end(); iter_v++) {
			// For each var, fetch procs that modify it
			sub_result = this->ModProcGivenVar(*iter_v);
			this->MergeMultiVector(result, sub_result);
		}
	}
	return result;
	
}


// Given a var, we want statements that modify it.
vector<vector<int>> ModifiesHandle::ModStatementsGivenVar
	(const VARINDEX var_index)
{
	vector<vector<int>> sub_result_set_container, 
		sub_result_set_assign, result_set;
	vector<int> left, right;

	result_set.push_back(left);
	result_set.push_back(right);
	
	// Fetch containers that modify given var
	sub_result_set_container = ModContainerGivenVar(var_index);
	sub_result_set_assign = ModAssignmentsGivenVar(var_index);

	// Merge
	this->MergeMultiVector(result_set, sub_result_set_container);
	this->MergeMultiVector(result_set, sub_result_set_assign);
	
	return result_set;
}


vector<vector<int>> ModifiesHandle::ModAssignmentsGivenVar(const VARINDEX var_index) 
{

	vector<vector<int>> result_set;
	vector<int> left;
	STMT_ARRAY assignment_stmts;

	// Fetch assignments that modify given var
	assignment_stmts = PKB::modifies.GetAssignmentModifiesVariable(var_index);

	// Store in first column of result set
	for(auto iter = assignment_stmts.begin(); iter!=assignment_stmts.end(); iter++) {
		left.push_back((*iter).statement_number);
	}
	result_set.push_back(left);
	return result_set;
	
}

vector<vector<int>> ModifiesHandle::ModIfGivenVar(const VARINDEX var_index) 
{
	vector<vector<int>> result_set;
	vector<int> left, right;
	STMT_ARRAY if_stmts;

	// Fetch If-stmts that modify given var
	if_stmts =  PKB::modifies.GetContainerModifiesVariable(var_index, IF);


	// Store in first column of result set
	for(auto iter = if_stmts.begin();iter!=if_stmts.end();iter++) {
	    left.push_back((*iter).statement_number);
		right.push_back(var_index);
	}

	result_set.push_back(left);
	result_set.push_back(right);

	return result_set;

}



vector<vector<int>> ModifiesHandle::ModWhileGivenVar(const VARINDEX var_index) 
{
	vector<vector<int>> result_set;
	vector<int> left, right;
	STMT_ARRAY if_stmts;

	// Fetch If-stmts that modify given var
	if_stmts =  PKB::modifies.GetContainerModifiesVariable(var_index, WHILE);

	// Store in first column of result set
	for(auto iter = if_stmts.begin();iter!=if_stmts.end();iter++) {
		left.push_back((*iter).statement_number);
		right.push_back(var_index);
	}

	result_set.push_back(left);
	result_set.push_back(right);

	return result_set;

}

vector<vector<int>> ModifiesHandle::ModContainerGivenVar(const VARINDEX var_index) 
{
	vector<vector<int>> result_set;
	vector<int> left, right;
	STMT_ARRAY if_stmts, while_stmts, con_stmts;

	// Fetch If-stmts that modify given var
	if_stmts = PKB::modifies.GetContainerModifiesVariable(var_index, IF);

	// Fetch while-statements that modify given var
	while_stmts = PKB::modifies.GetContainerModifiesVariable(var_index, WHILE);

	set_union(if_stmts.begin(), if_stmts.end(), while_stmts.begin(), while_stmts.end(), inserter(con_stmts, con_stmts.begin()));

	// Store in first column of result set
	for(auto iter = con_stmts.begin(); iter!=con_stmts.end(); iter++) {
		left.push_back((*iter).statement_number);
		right.push_back(var_index);
	}

	result_set.push_back(left);
	result_set.push_back(right);

	return result_set;

}




//      [ v1, v2 ] 
//        1    1 
//        1    2
//        1    3

// Handles retrieval of all statements and the variables they modify
/*vector<vector<int>> ModifiesHandle::ModStatementsAndVars() 
{
	vector<vector<int>> result_set;
	INTEGER_ARRAY all_stmts = pkb->GetAllStatement();
	NodeType type;
	STMT temp;
	VARINDEX_ARRAY var_array;
	VARINDEX var;

	for_each(all_stmts.begin(), all_stmts.end(), 
		[this, &var, &var_array, &temp, &type, &result_set](int stmt_num) {
		temp.statement_number = stmt_num;
		type = PKB::shortcutTable.GetStatementType(stmt_num);
		switch(type) {
		case ASSIGN: {
			temp.node_type = ASSIGN;
			var = pkb->GetVariableModifiedByAssignment(temp);
			result_set[0].push_back(stmt_num);
			result_set[1].push_back(var);
			break;
		}
		case IF: { 
			temp.node_type = IF;
			var_array = pkb->GetVariablesModifiedByContainer(temp);
			result_set[0].push_back(stmt_num);
			for(auto iter = var_array.begin(); iter!=var_array.end(); iter++) {
				result_set[0].push_back(stmt_num);
				result_set[1].push_back((*iter));
			}
			break;
			
		}
		case WHILE: {
			temp.node_type = WHILE;
			var_array = pkb->GetVariablesModifiedByContainer(temp);
			result_set[0].push_back(stmt_num);
			for(auto iter = var_array.begin(); iter!=var_array.end(); iter++) {
				result_set[0].push_back(stmt_num);
				result_set[1].push_back((*iter));
			}
			break;
		}
		}
	});

	return result_set;

}*/

// Retrieves all assignments and the variables they modify
/*vector<vector<int>> ModifiesHandle::ModAssignmentsAndVars() 
{
	vector<vector<int>> result_set;
	INTEGER_ARRAY all_stmts = pkb->GetAllStatement();
	NodeType type;
	STMT temp;
	VARINDEX_ARRAY var_array;
	VARINDEX var;

	for_each(all_stmts.begin(), all_stmts.end(), 
		[this, &var, &var_array, &temp, &type, &result_set](int stmt_num) {
		temp.statement_number = stmt_num;
		type = PKB::shortcutTable.GetStatementType(stmt_num);
		switch(type) {
		case ASSIGN: {
			temp.node_type = ASSIGN;
			var = pkb->GetVariableModifiedByAssignment(temp);
			result_set[0].push_back(stmt_num);
			result_set[1].push_back(var);
			break;
		}
		
		}
	});

	return result_set;

}*/


// Retrieves all if statements all the variables they modify
/*vector<vector<int>> ModifiesHandle::ModIfAndVar() 
{
	vector<vector<int>> result_set;
	INTEGER_ARRAY all_stmts = pkb->GetAllStatement();
	NodeType type;
	STMT temp;
	VARINDEX_ARRAY var_array;
	VARINDEX var;

	for_each(all_stmts.begin(), all_stmts.end(), 
		[this, &var, &var_array, &temp, &type, &result_set](int stmt_num) {
		temp.statement_number = stmt_num;
		type = PKB::shortcutTable.GetStatementType(stmt_num);
		switch(type) {
		
		case IF: { 
			temp.node_type = IF;
			var_array = pkb->GetVariablesModifiedByContainer(temp);
			result_set[0].push_back(stmt_num);
			for(auto iter = var_array.begin(); iter!=var_array.end(); iter++) {
				result_set[0].push_back(stmt_num);
				result_set[1].push_back((*iter));
			}
			break;
			
		}
		
		}
	});

	return result_set;

}*/


// Retrieves all While statements and the variables they modify
/*vector<vector<int>> ModifiesHandle::ModWhileAndVar() 
{
	vector<vector<int>> result_set;
	INTEGER_ARRAY all_stmts = pkb->GetAllWhile();
	NodeType type;
	STMT temp;
	VARINDEX_ARRAY var_array;
	VARINDEX var;

	for_each(all_stmts.begin(), all_stmts.end(), 
		[this, &var, &var_array, &temp, &type, &result_set](int stmt_num) {
		temp.statement_number = stmt_num;
		type = PKB::shortcutTable.GetStatementType(stmt_num);
		switch(type) {
		
		case WHILE: { 
			temp.node_type = WHILE;
			var_array = pkb->GetVariablesModifiedByContainer(temp);
			result_set[0].push_back(stmt_num);
			for(auto iter = var_array.begin(); iter!=var_array.end(); iter++) {
				result_set[0].push_back(stmt_num);
				result_set[1].push_back((*iter));
			}
			break;
			
		}
		
		}
	});

	return result_set;

}
*/
/*
vector<vector<int>> ModifiesHandle::ModContainerAndVar()
{
	vector<vector<int>> result_set, sub_result_set_if, sub_result_set_while;
	sub_result_set_if = ModIfAndVar();
	sub_result_set_while = ModWhileAndVar();
	merge (sub_result_set_if.begin(), sub_result_set_if.end(),
		sub_result_set_while.begin(), sub_result_set_while.end(),
		result_set.begin());

	return result_set;
}*/


// Retrieves all Variables modified by a given statement
vector<vector<int>> ModifiesHandle::ModVarsGivenStatement(const int stmt_num) 
{
	vector<vector<int>> result_set;
	vector<int> left, right;
	VARINDEX_ARRAY var_array;
	VARINDEX var;
	STMT temp;
	temp.statement_number = stmt_num;
	NodeType type = PKB::shortcutTable.GetStatementType(stmt_num);

	switch(type) {
	case IF:
		temp.node_type = IF;
		var_array = PKB::modifies.GetVariablesModifiedByContainer(temp);
		for(auto iter = var_array.begin(); iter!=var_array.end() ;  iter++) {
			left.push_back(stmt_num);
			right.push_back(*iter);
		}
		break;
	case WHILE:
		temp.node_type = WHILE;
		var_array = PKB::modifies.GetVariablesModifiedByContainer(temp);
		for(auto iter = var_array.begin(); iter!=var_array.end() ;  iter++) {
			left.push_back(stmt_num);
			right.push_back(*iter);
		}
		break;
	case ASSIGN:
		temp.node_type = ASSIGN;
		var = PKB::modifies.GetVariableModifiedByAssignment(temp);
		left.push_back(stmt_num);
		right.push_back(var);
		break;

   }
	result_set.push_back(left);
	result_set.push_back(right);
	return result_set;
}


// Retrieves all Procedures and the Variables they modify
/*vector<vector<int>> ModifiesHandle::ModProcAndVars() 
{
	PROCINDEX_ARRAY proc_array = pkb->GetAllProcIndex();
	VARINDEX_ARRAY var_array;
	vector<vector<int>> result_set;
	for_each(proc_array.begin(), proc_array.end(), 
		[this, &result_set, &var_array](PROCINDEX proc) {

		var_array = pkb->GetVariablesModifiedByProcedure(proc);
		for(auto iter = var_array.begin(); iter != var_array.end(); iter++) {
			result_set[0].push_back(proc);
			result_set[1].push_back(*iter);
		}
	});

	return result_set;
}*/


// Retrieves all Variables modified by given a Procedure
vector<vector<int>> ModifiesHandle::ModVarsGivenProc(const PROCINDEX proc_index) 
{
	vector<vector<int>> result_set;
	vector<int> left, right;

	// Fetch variables Modified by given Proc
	VARINDEX_ARRAY var_array = PKB::modifies.GetVariablesModifiedByProcedure(proc_index);

	// Populate result_set
	for (auto iter = var_array.begin(); iter!=var_array.end();  iter++) {
		left.push_back(proc_index);
		right.push_back(*iter);
	}
	result_set.push_back(left);
	result_set.push_back(right);
	return result_set;
}


// Retrieves all Procedures that modify a given Variable
vector<vector<int>> ModifiesHandle::ModProcGivenVar(const VARINDEX var_index) 
{
	vector<vector<int>> result_set;
	vector<int> left, right;
	PROCINDEX_ARRAY proc_array;

	proc_array = PKB::modifies.GetProceduresModifiesVariable(var_index);

	for (auto iter=proc_array.begin(); iter!=proc_array.end(); iter++) {
		left.push_back(*iter);
		right.push_back(var_index);

	}

	result_set.push_back(left);
	result_set.push_back(right);

	return result_set;
}

